/*
 * Copyright Aristool AG Switzerland
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
package org.opprua.tidy.util;

import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * TODO: add Java doc
 *
 * Prepared for first commit: 15.04.2007
 *
 * @author Lin Li
 */
public class FileUtil {

  public static boolean exists(String name) {
		if (name == null || name.trim().length() == 0) {
			return false;
		}

		StringTokenizer st = new StringTokenizer(name, "/\\");

		File dir = null;
		while (st.hasMoreTokens()) {
			String t = st.nextToken();
			if (dir == null) {
				dir = new File(t);
			} else {
				if (!dir.exists()) {
					return false;
				}
				dir = new File(dir, t);
			}
		}
		return dir.exists();
	}

	/**
	 * Creates a file and returns a PrintWriter to the file. If the name contains
	 * directories and these are not there yet, they will be created. If the file
	 * already exists, it will be overwritten.
	 * 
	 * @param name path to the file to be written
	 * @return the PrintWriter to write to the file.
	 */
	public static PrintWriter getWriter(String name) {
		prepare(name);
		try {
			return new PrintWriter(new FileWriter(name));
		} catch (Throwable e) {
			throw new Error("Can not create file for write: " + name, e);
		}
	}

	/**
	 * Read the entire content of a text file.
	 * 
	 * @param name path to the file
	 * @return the string with the content of the file
	 */
	public static String getFile(String name) {
		try {
			StringBuffer buf = new StringBuffer();
			BufferedReader reader = new BufferedReader(new FileReader(name));
			while (reader.ready()) {
				buf.append(reader.readLine()).append("\n");
			}
			reader.close();
			return buf.toString();
		} catch (Throwable e) {
			throw new Error("Can not read file: " + name, e);
		}
	}

	/**
	 * Prepares the directory for the creation of a file. If the given
	 * path contains directories that do not yet exist, they will be created
	 * one by one until the file in the path can be created in the correct
	 * directory (but is itself not created).
	 * 
	 * @param path path to the file to be created
	 * @return the path.
	 */
	public static String prepare(String path) {
		StringTokenizer st = new StringTokenizer(path, "/\\");

		File dir = null;
		while (st.hasMoreTokens()) {
			String t = st.nextToken();
			if (dir == null) {
				dir = new File(t);
				continue;
			} else {
				if (!dir.exists() && !dir.equals(new File(path))) {
					dir.mkdir();
				}
				dir = new File(dir, t);
			}
		}
		return path;
	}

	/**
	 * Copies a file with path source to the file with path destination. If the
	 * destination file does not exist, it will be created. If it already exists,
	 * it will be overwritten. If the destination path contains directories that 
	 * do not exist, these will be created as well.
	 * 
	 * @param source path to the source file
	 * @param destination path to the destination file
	 */
	public static void copyFile(String source, String destination) {
		prepare(destination);
		safeCopy(new File(source), new File(destination));
	}

	/**
	 * <p>Copy all files in a directory to a new directory. If the new directory
	 * does not exist, it will be created. The copy can be recursive.</p>
	 * 
	 * <p>When the copy is recursive, the directories with name "CVS" are left
	 * out. This is to prevent overwritting the CVS connections already in
	 * place.</p>
	 * 
	 * @param from directory containing source files
	 * @param to directory to copy the files to
	 * @param recursive whether subdirectories in the source should also be 
	 * copied into subdirectory of the destination.
	 */
	public static void copyDir(String from, String to, boolean recursive) {

		if (from.endsWith(".jar") || from.endsWith(".zip")) {
			try {
				ZipFile j = new ZipFile(from);
				Enumeration<? extends ZipEntry> e = j.entries();
				while (e.hasMoreElements()) {
					ZipEntry entry = (ZipEntry) e.nextElement();
					if (entry.isDirectory()) {
						continue;
					}

					String name = entry.getName();
					if (!recursive && name.indexOf("/") > 0) {
						continue;
					}

					try {
						InputStream in = j.getInputStream(entry);
						prepare(to + name); // make sure the last stop dir is created. 
						OutputStream out = new FileOutputStream(to + name);
						safeCopy(in, out);
					} catch (Throwable ex) {
						throw new Error(
							"Failed to copy zipped files from " + from + " to " + to,
							ex);
					}

				}
				return;
			} catch (Throwable e) {
				throw new Error("Failed to manage the zip file: " + from, e);
			}
		}

		File fdir = new File(from);
		if (!fdir.isDirectory()) {
			throw new Error("Failed to find the source directory for copying");
		}

		File[] files = fdir.listFiles();
		prepare(to + "/something"); // make sure the last stop dir is created. 

		for (int i = 0; i < files.length; i++) {
			if (files[i].isDirectory()) {
				if (!files[i].getName().equals("CVS") && recursive) {
					copyDir(files[i].getPath(), to + "/" + files[i].getName(), true);
				}
			} else {
				copyFile(files[i].getPath(), to + "/" + files[i].getName());
			}
		}
	}

	private static void safeCopy(File src, File des) {
		try {
			InputStream in = new FileInputStream(src);
			OutputStream out = new FileOutputStream(des);
			safeCopy(in, out);
		} catch (Throwable e) {
			throw new Error(
				"Failed to copy files from " + src.getPath() + " to " + des.getPath(),
				e);
		}
	}

	private static void safeCopy(InputStream in, OutputStream out) {
		try {

			// Transfer bytes from in to out
			byte[] buf = new byte[1024];
			int len;
			while ((len = in.read(buf)) > 0) {
				out.write(buf, 0, len);
			}
			in.close();
			out.close();
		} catch (Throwable e) {
			throw new Error("Failed to copy file", e);
		}
	}
}
